package cn.ibizlab.util.service;

import cn.ibizlab.util.domain.AuditItem;
import cn.ibizlab.util.domain.EntityBase;
import cn.ibizlab.util.domain.LoggingEvent;
import cn.ibizlab.util.helper.BeanCache;
import cn.ibizlab.util.mapper.LoggingEventMapper;
import cn.ibizlab.util.security.AuthenticationUser;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 实体[DataAudit] 服务对象接口实现
 */
@Service
@Slf4j
public class SimpleAuditService implements IBZDataAuditService {

    private static SimpleAuditService instance = null;

    public static SimpleAuditService getInstance() {
        return instance;
    }

    @PostConstruct
    void init() {
        SimpleAuditService.instance = this;
    }

    private static List<LoggingEvent> cacheMap = Collections.synchronizedList(new ArrayList<>());
    /**
     * 定时保存审计记录
     */
    @Scheduled(fixedRate = 10000)
    private void saveAudit() {
        if(!cacheMap.isEmpty()) {
            log.info(String.format("正在保存审计数据，当前审计集合数量为[%s]",cacheMap.size()));
            List<LoggingEvent> temp=new ArrayList<>();
            if(cacheMap.size()<500) {
                temp.addAll(cacheMap);
            }
            else {
                temp.addAll(cacheMap.subList(0,500));
            }
            temp.forEach(item -> log.debug("Audit:{}-{}:{}:{}-{}",item.getLoggerName(),item.getCallerFilename(),item.getArg0(),item.getArg1(),item.getArg2()));

            if(isSave2db())
                LoggingEvent.saveBatch(temp);
            cacheMap.removeAll(temp);
            log.info(String.format("保存完成，当前审计集合数量为[%s]",cacheMap.size()));
        }
    }

    @Autowired
    private LoggingEventMapper loggingEventMapper;

    private Integer ignore2db;
    private boolean isSave2db() {
        if(ignore2db==null) {
            try {
                ignore2db = loggingEventMapper.checkLoggingEvent();
            }catch (Exception ex) {
                ignore2db = 1;
            }
        }
        return ignore2db==0;
    }


    private LoggingEvent newAudit(HttpServletRequest request) {
        AuthenticationUser curUser = AuthenticationUser.getAuthenticationUser();
        LoggingEvent dataAudit = new LoggingEvent().setLevelString("TRACE").setTimeStamp(System.currentTimeMillis()).setReferenceFlag(1024).setArg1(curUser.getUserid()).setArg2(curUser.getDisplayName()).setArg3("1");
        if(request != null) {
            dataAudit.setThreadName(getIpAddress(request, curUser));
        }
        return dataAudit;
    }


    @Override
    public void createAudit(HttpServletRequest request, EntityBase entity, Object idValue) {
        BeanCache.BeanSchema schema = BeanCache.get(entity.getClass());
        if(ObjectUtils.isEmpty(idValue))
            idValue=entity.get(schema.getKeyField().getCodeName());

        LoggingEvent dataAudit = newAudit(request).setCallerFilename(schema.getCodeName()).setCallerClass(entity.getClass().getName()).setCallerMethod("create")
                .setLoggerName(schema.getLogicName()+" 新建").setArg0((String)idValue);

        getAuditInfo(entity,schema.getAudits()).forEach(item -> dataAudit.set(item.getId(),item));
        cacheMap.add(dataAudit);
    }


    @SneakyThrows
    public void updateAudit(HttpServletRequest request, EntityBase beforeEntity, EntityBase afterEntity, Object idValue) {
        BeanCache.BeanSchema schema = BeanCache.get(beforeEntity.getClass());
        if(ObjectUtils.isEmpty(idValue))
            idValue=beforeEntity.get(schema.getKeyField().getCodeName());
        //获取更新后的审计内容
        List<AuditItem> auditInfo = getUpdateAuditInfo(beforeEntity, afterEntity, schema.getAudits());//比较更新前后差异内容

        LoggingEvent dataAudit = newAudit(request).setCallerFilename(schema.getCodeName()).setCallerClass(beforeEntity.getClass().getName()).setCallerMethod("update")
                .setLoggerName(schema.getLogicName()+" 更新").setArg0((String)idValue).setArg3(ObjectUtils.isEmpty(auditInfo)?"0":"1");

        auditInfo.forEach(item -> dataAudit.set(item.getId(),item));
        cacheMap.add(dataAudit);
    }


    public void removeAudit(HttpServletRequest request, EntityBase entity, Object idValue) {
        BeanCache.BeanSchema schema = BeanCache.get(entity.getClass());
        if(ObjectUtils.isEmpty(idValue))
            idValue=entity.get(schema.getKeyField().getCodeName());

        LoggingEvent dataAudit = newAudit(request).setCallerFilename(schema.getCodeName()).setCallerClass(entity.getClass().getName()).setCallerMethod("remove")
                .setLoggerName(schema.getLogicName()+" 删除").setArg0((String)idValue);

        cacheMap.add(dataAudit);
    }

    @Override
    public void customAudit(String caller, String method, Object idValue, EntityBase entity, String name, HttpServletRequest request) {

        LoggingEvent dataAudit = newAudit(request).setCallerFilename(caller).setCallerClass(caller).setCallerMethod(method)
                .setLoggerName(name).setArg0((String)idValue);

        if(entity!=null) {
            BeanCache.BeanSchema schema = BeanCache.get(entity.getClass());
            if(ObjectUtils.isEmpty(idValue))
                idValue=entity.get(schema.getKeyField().getCodeName());
            dataAudit.setCallerClass(entity.getClass().getName()).setLoggerName(schema.getLogicName()+(ObjectUtils.isEmpty(name)?"":(" "+name))).setArg0((String)idValue);
            getAuditInfo(entity,schema.getAudits()).forEach(item -> dataAudit.set(item.getId(),item));
        }

        cacheMap.add(dataAudit);
    }
}